<?php

namespace App;

use Swoole\Coroutine as Co;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Database\RedisConfig;
use Swoole\Database\RedisPool;
use Swoole\Timer;
use App\Timer\FdClear;
use App\Open;
use App\Close;

class Ws
{
    private $mode = 'dev';
    private $config = [];
    public $server = null;
    static public $table = null;
    static public $dbPool = null;
    static public $redisPool = [];

    public function __construct($mode)
    {
        // 环境模式
        $this->mode = $mode;
        // 配置文件
        $this->config = include_once "./config_{$this->mode}.php";
        // websocket 配置
        $this->setServer();
        // mysql 配置
        $this->setDb();
        // redis 配置
        $this->setRedis();
        // table 配置
        $table = include_once('App/table.php');
        self::$table = $table;
        // 定时器 配置
        $this->timer();
        $this->server->on('open', [$this, 'onOpen']);
        $this->server->on('message', [$this, 'onMessage']);
        $this->server->on('task', [$this, 'onTask']);
        $this->server->on("finish", [$this, 'onFinish']);
        $this->server->on('close', [$this, 'onClose']);
        $this->server->start();
    }

    private function setServer()
    {
        $this->server = new \Swoole\WebSocket\Server("0.0.0.0", $this->config['server']);
        $this->server->set($this->config['ws']);
    }

    private function setDb()
    {
        $config = $this->config['pdo'];
        Co\run(function () use ($config) {
            $pool = new PDOPool((new PDOConfig)
                    ->withHost($config['host'])
                    ->withPort($config['port'])
                    ->withDbName($config['dbName'])
                    ->withCharset($config['charSet'])
                    ->withUsername($config['userName'])
                    ->withPassword($config['password']),
                $config['poolSize']
            );
            self::$dbPool = $pool;
        });
    }

    private function setRedis()
    {
        $config = $this->config['redis'];
        Co\run(function () use ($config) {
            foreach ($config as $key => $item) {
                $redisPool = new RedisPool((new RedisConfig)
                        ->withHost($item['host'])
                        ->withPort($item['port'])
                        ->withAuth($item['auth'])
                        ->withDbIndex($item['db'])
                        ->withTimeout($item['timeout'])
                );
                self::$redisPool[$key] = $redisPool;
            }
        });
    }

    private function timer()
    {
        $server = $this->server;
        // 删除15分钟内没有操作的客户端连接
        $expired = 15 * 60;
        Timer::tick($expired * 1000, function () use ($server, $expired) {
            (new FdClear($server))->deletFdAfterSecond($expired);
        });
    }

    public function onOpen($server, $request)
    {
        go(function () use ($server, $request) {
            new Open($server, $request);
        });
    }

    public function onMessage($server, $frame)
    {
        // 获取客户端传入的数据
        if (empty($frame->data)) {
            return;
        }
        $info = json_decode($frame->data, true);
        // 解析数据获取分发的类
        $class = $info['class'] ?? 'Index';
        // 解析数据获取解析的方法
        $method = $info['type'] ?? 'index';
        // 引入分发的类
        $className = "App\\Message\\{$class}";
        if (!class_exists($className)) {
            if ($this->mode === 'dev') {
                print 'Class ' . $className . ' is not exist.' . "\r\n";
            }
            return;
        }
        // 解析客户端数据
        if (in_array($method, get_class_methods($className))) {
            $fd = $frame->fd;
            go(function () use ($className, $method, $server, $info, $fd) {
                (new $className($server, $fd, $info))->$method();
            });
        } else {
            if ($this->mode === 'dev') {
                print 'Class ' . $className . ' method ' . $method . " is not exist." . "\r\n";
            }
            return;
        }
    }

    public function onTask($serv, $task_id, $worker_id, $data)
    {
        return "success";
    }

    public function onFinish($serv, $task_id, $data)
    {
        echo "完成任务{$task_id}投递 处理结果：{$data}\r\n";
    }

    public function onClose($server, $fd)
    {
        go(function () use ($server, $fd) {
            new Close($server, $fd);
        });
    }
}
